home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / selection.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-09  |  9.1 KB  |  423 lines

  1.  
  2.  
  3. #include <stdlib.h>
  4.  
  5. #include <X11/Xatom.h>
  6.  
  7. #include "WINGsP.h"
  8.  
  9. #define MAX_PROPERTY_SIZE 8*1024
  10.  
  11.  
  12. typedef struct SelectionHandler {
  13.     WMWidget *widget;
  14.     Atom selection;
  15.     Time timestamp;
  16.     WMConvertSelectionProc *convProc;
  17.     WMLoseSelectionProc *loseProc;
  18.     WMSelectionDoneProc *doneProc;
  19.  
  20.     struct {
  21.     unsigned delete_pending:1;
  22.     unsigned done_pending:1;
  23.     } flags;
  24.  
  25.     struct SelectionHandler *next;
  26. } SelectionHandler;
  27.  
  28.  
  29. static SelectionHandler *selHandlers = NULL;
  30.  
  31.  
  32. void
  33. WMDeleteSelectionHandler(WMWidget *widget, Atom selection)
  34. {
  35.     SelectionHandler *handler, *tmp;
  36.     Display *dpy = WMWidgetScreen(widget)->display;
  37.     Window win = WMWidgetXID(widget);
  38.     Time timestamp;
  39.  
  40.     if (!selHandlers)
  41.         return;
  42.  
  43.     tmp = selHandlers;
  44.  
  45.     if (tmp->widget == widget) {
  46.  
  47.     if (tmp->flags.done_pending) {
  48.         tmp->flags.delete_pending = 1;
  49.         return;
  50.     }
  51.         selHandlers = tmp->next;
  52.     timestamp = tmp->timestamp;
  53.         wfree(tmp);
  54.     } else {
  55.         while (tmp->next) {
  56.             if (tmp->next->widget == widget) {
  57.  
  58.         if (tmp->next->flags.done_pending) {
  59.             tmp->next->flags.delete_pending = 1;
  60.             return;
  61.         }
  62.                 handler = tmp->next;
  63.                 tmp->next = handler->next;
  64.         timestamp = handler->timestamp;
  65.                 wfree(handler);
  66.                 break;
  67.             }
  68.             tmp = tmp->next;
  69.         }
  70.     }
  71.  
  72.     XGrabServer(dpy);
  73.     if (XGetSelectionOwner(dpy, selection) == win) {
  74.     XSetSelectionOwner(dpy, selection, None, timestamp);
  75.     }
  76.     XUngrabServer(dpy);
  77. }
  78.  
  79.  
  80. static Bool gotError = 0;
  81. /*
  82. static int
  83. errorHandler(XErrorEvent *error)
  84. {
  85.     return 0;
  86. }
  87. */
  88.  
  89. static Bool
  90. writeSelection(Display *dpy, Window requestor, Atom property, Atom type,
  91.            void *value, long length, int format)
  92. {
  93. /*
  94.     printf("write to %x: %s\n", requestor, XGetAtomName(dpy, property));
  95. */
  96.     gotError = False;
  97.  
  98. #ifndef __sgi
  99.     if (!XChangeProperty(dpy, requestor, property, type, format,
  100.                          PropModeReplace, value, length))
  101.         return False;
  102. #else
  103.     /* in sgi seems this seems to return void */
  104.     XChangeProperty(dpy, requestor, property, type, format,
  105.             PropModeReplace, value, length);
  106. #endif
  107.  
  108.     XFlush(dpy);
  109.  
  110.     return !gotError;
  111. }
  112.  
  113.  
  114. static void
  115. notifySelection(XEvent *event, Atom prop)
  116. {
  117.     XEvent ev;
  118. /*
  119.     printf("envent to %x\n", event->xselectionrequest.requestor);
  120. */
  121.     ev.xselection.type = SelectionNotify;
  122.     ev.xselection.serial = 0;
  123.     ev.xselection.send_event = True;
  124.     ev.xselection.display = event->xselectionrequest.display;
  125.     ev.xselection.requestor = event->xselectionrequest.requestor;
  126.     ev.xselection.target = event->xselectionrequest.target;
  127.     ev.xselection.selection = event->xselectionrequest.selection;
  128.     ev.xselection.property = prop;
  129.     ev.xselection.time = event->xselectionrequest.time;
  130.     
  131.     XSendEvent(event->xany.display, event->xselectionrequest.requestor, 
  132.            False, 0, &ev);
  133.     XFlush(event->xany.display);
  134. }
  135.  
  136.  
  137. void
  138. W_HandleSelectionEvent(XEvent *event)
  139. {
  140.     SelectionHandler *handler;
  141.  
  142.     handler = selHandlers;
  143.  
  144.     while (handler) {
  145.     if (WMWidgetXID(handler->widget)==event->xany.window
  146. /*        && handler->selection == event->selection*/) {
  147.  
  148.         switch (event->type) {
  149.          case SelectionClear:
  150.         if (handler->loseProc)
  151.             (*handler->loseProc)(handler->widget, handler->selection);
  152.         break;
  153.  
  154.          case SelectionRequest:
  155.         if (handler->convProc) {
  156.             Atom atom;
  157.             void *data;
  158.             unsigned length;
  159.             int format;
  160.             Atom prop;
  161.  
  162.             /* they're requesting for something old */
  163.             if (event->xselectionrequest.time < handler->timestamp
  164.             && event->xselectionrequest.time != CurrentTime) {
  165.  
  166.             notifySelection(event, None);
  167.             break;
  168.             }
  169.  
  170.             handler->flags.done_pending = 1;
  171.  
  172.             if (!(*handler->convProc)(handler->widget,
  173.                          handler->selection,
  174.                          event->xselectionrequest.target,
  175.                          &atom, &data, &length, &format)) {
  176.  
  177.             notifySelection(event, None);
  178.             break;
  179.             }
  180.  
  181.             
  182.             prop = event->xselectionrequest.property;
  183.             /* obsolete clients that don't set the property field */
  184.             if (prop == None)
  185.             prop = event->xselectionrequest.target;
  186.  
  187.             if (!writeSelection(event->xselectionrequest.display,
  188.                     event->xselectionrequest.requestor,
  189.                     prop, atom, data, length, format)) {
  190.  
  191.             wfree(data);
  192.             notifySelection(event, None);
  193.             break;
  194.             }
  195.             wfree(data);
  196.  
  197.             notifySelection(event, prop);
  198.  
  199.             if (handler->doneProc) {
  200.             (*handler->doneProc)(handler->widget, 
  201.                          handler->selection,
  202.                          event->xselectionrequest.target);
  203.             }
  204.  
  205.             handler->flags.done_pending = 0;
  206.  
  207.             /* in case the handler was deleted from some
  208.              * callback */
  209.             if (handler->flags.delete_pending) {
  210.             WMDeleteSelectionHandler(handler->widget,
  211.                          handler->selection);
  212.             }
  213.         }
  214.         break;
  215.  
  216.          case SelectionNotify:
  217.         
  218.         break;
  219.         }
  220.     }
  221.  
  222.     handler = handler->next;
  223.     }
  224. }
  225.  
  226.  
  227.  
  228.  
  229. Bool
  230. WMCreateSelectionHandler(WMWidget *w, Atom selection, Time timestamp,
  231.              WMConvertSelectionProc *convProc,
  232.              WMLoseSelectionProc *loseProc,
  233.              WMSelectionDoneProc *doneProc)
  234. {
  235.     SelectionHandler *handler, *tmp;
  236.     Display *dpy = WMWidgetScreen(w)->display;
  237.  
  238.     XSetSelectionOwner(dpy, selection, WMWidgetXID(w), timestamp);
  239.     if (XGetSelectionOwner(dpy, selection) != WMWidgetXID(w))
  240.     return False;
  241.  
  242.     handler = malloc(sizeof(SelectionHandler));
  243.     if (!handler)
  244.     return False;
  245.  
  246.     handler->widget = w;
  247.     handler->selection = selection;
  248.     handler->timestamp = timestamp;
  249.     handler->convProc = convProc;
  250.     handler->loseProc = loseProc;
  251.     handler->doneProc = doneProc;
  252.     memset(&handler->flags, 0, sizeof(handler->flags));
  253.  
  254.     if (!selHandlers) {
  255.     /* first in the queue */
  256.     handler->next = selHandlers;
  257.     selHandlers = handler;
  258.     } else {
  259.     tmp = selHandlers;
  260.     while (tmp->next) {
  261.         tmp = tmp->next;
  262.     }
  263.     handler->next = tmp->next;
  264.     tmp->next = handler;
  265.     }
  266.  
  267.     return True;
  268. }
  269.  
  270.  
  271.  
  272.  
  273. static void
  274. timeoutHandler(void *data)
  275. {
  276.     *(int*)data = 1;
  277. }
  278.  
  279.  
  280. static Bool
  281. getInternalSelection(WMScreen *scr, Atom selection, Atom target,
  282.              void **data, unsigned *length)
  283. {
  284.     Window owner;
  285.     SelectionHandler *handler;
  286.  
  287.     /*
  288.      * Check if the selection is owned by this application and if so,
  289.      * do the conversion directly.
  290.      */
  291.  
  292.     *data = NULL;
  293.  
  294.     owner = XGetSelectionOwner(scr->display, selection);
  295.     if (!owner)
  296.     return False;
  297.  
  298.     handler = selHandlers;
  299.  
  300.     while (handler) {
  301.     if (WMWidgetXID(handler->widget) == owner
  302.         /*        && handler->selection == event->selection*/) {
  303.         break;
  304.     }
  305.     handler = handler->next;
  306.     }
  307.  
  308.     if (!handler)
  309.     return False;
  310.  
  311.     if (handler->convProc) {
  312.     Atom atom;
  313.     int format;
  314.  
  315.     if (!(*handler->convProc)(handler->widget, handler->selection, 
  316.                   target, &atom, data, length, &format)) {
  317.         return True;
  318.     }
  319.  
  320.     if (handler->doneProc) {
  321.         (*handler->doneProc)(handler->widget, handler->selection, target);
  322.     }
  323.     }
  324.  
  325.     return True;
  326. }
  327.  
  328.  
  329. char*
  330. W_GetTextSelection(WMScreen *scr, Atom selection)
  331. {
  332.     int buffer = -1;
  333.     
  334.     switch (selection) {
  335.      case XA_CUT_BUFFER0:
  336.     buffer = 0;
  337.     break;
  338.      case XA_CUT_BUFFER1:
  339.     buffer = 1;
  340.     break;
  341.      case XA_CUT_BUFFER2:
  342.     buffer = 2;
  343.     break;
  344.      case XA_CUT_BUFFER3:
  345.     buffer = 3;
  346.     break;
  347.      case XA_CUT_BUFFER4:
  348.     buffer = 4;
  349.     break;
  350.      case XA_CUT_BUFFER5:
  351.     buffer = 5;
  352.     break;
  353.      case XA_CUT_BUFFER6:
  354.     buffer = 6;
  355.     break;
  356.      case XA_CUT_BUFFER7:
  357.     buffer = 7;
  358.     break;
  359.     }
  360.     if (buffer >= 0) {
  361.     char *data;
  362.     int size;
  363.  
  364.     data = XFetchBuffer(scr->display, &size, buffer);
  365.  
  366.     return data;
  367.     } else {
  368.     char *data;
  369.     int bits;
  370.     Atom rtype;
  371.     unsigned long len, bytes;
  372.     WMHandlerID timer;
  373.     int timeout = 0;
  374.     XEvent ev;
  375.     unsigned length;
  376.     
  377.     XDeleteProperty(scr->display, scr->groupLeader, scr->clipboardAtom);
  378.  
  379.     if (getInternalSelection(scr, selection, XA_STRING, (void**)&data,
  380.                  &length)) {
  381.  
  382.         return data;
  383.     }
  384.     
  385.     XConvertSelection(scr->display, selection, XA_STRING,
  386.               scr->clipboardAtom, scr->groupLeader,
  387.               scr->lastEventTime);
  388.     
  389.     timer = WMAddTimerHandler(1000, timeoutHandler, &timeout);
  390.     
  391.     while (!XCheckTypedWindowEvent(scr->display, scr->groupLeader,
  392.                        SelectionNotify, &ev) && !timeout);
  393.     
  394.     if (!timeout) {
  395.         WMDeleteTimerHandler(timer);
  396.     } else {
  397.         wwarning("selection retrieval timed out");
  398.         return NULL;
  399.     }
  400.  
  401.     /* nobody owns the selection or the current owner has
  402.      * nothing to do with what we need */
  403.     if (ev.xselection.property == None) {
  404.         return NULL;
  405.     }
  406.  
  407.     if (XGetWindowProperty(scr->display, scr->groupLeader, 
  408.                    scr->clipboardAtom, 0, MAX_PROPERTY_SIZE, 
  409.                    False, XA_STRING, &rtype, &bits, &len,
  410.                    &bytes, (unsigned char**)&data)!=Success) {
  411.         return NULL;
  412.     }
  413.     if (rtype!=XA_STRING || bits!=8) {
  414.         wwarning("invalid data in text selection");
  415.         if (data)
  416.         XFree(data);
  417.         return NULL;
  418.     }
  419.     return data;
  420.     }
  421. }
  422.  
  423.